package org.springframework.boot.autoconfigure.klock.core;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.klock.config.KlockConfig;
import org.springframework.boot.autoconfigure.klock.lock.Lock;
import org.springframework.boot.autoconfigure.klock.lock.LockFactory;
import org.springframework.boot.autoconfigure.klock.model.LockInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author ygm
 * @Date 2022/8/9
 */
@Slf4j
public class KlockHandler implements ApplicationContextAware {
    protected static final Map<String, KlockAspectHandler.LockRes> currentThreadLock = new ConcurrentHashMap<>();

    protected static ApplicationContext applicationContext;
    protected static LockFactory lockFactory;
    protected static KlockConfig klockConfig;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        KlockHandler.applicationContext = applicationContext;
        KlockHandler.lockFactory = applicationContext.getBean(LockFactory.class);
        KlockHandler.klockConfig = applicationContext.getBean(KlockConfig.class);
    }

    protected static LockRes getLock(LockInfo lockInfo) {
        LockRes lockRes = currentThreadLock.computeIfAbsent(lockInfo.getCurrentLockId(), s -> {
            LockRes newRes = new LockRes(lockInfo);
            Lock lock = lockFactory.getLock(lockInfo);
            newRes.setLock(lock);
            return newRes;
        });
        return lockRes;
    }

    protected static void cleanUpThreadLocal(String curentLock) {
        LockRes lockRes = currentThreadLock.get(curentLock);
        if (lockRes != null && lockRes.getLevelCount() <= 0) {
            log.debug("clean lock " + curentLock);
            currentThreadLock.remove(curentLock);
        }
    }

    protected static void cleanUpThreadLocal(LockInfo lockInfo) {
        cleanUpThreadLocal(lockInfo.getCurrentLockId());
    }

    public static class LockRes {

        private LockInfo lockInfo;
        private Lock lock;
        private int levelCount;

        LockRes(LockInfo lockInfo) {
            this.lockInfo = lockInfo;
            this.levelCount = 0;
        }

        LockInfo getLockInfo() {
            return lockInfo;
        }

        public Lock getLock() {
            return lock;
        }

        public void setLock(Lock lock) {
            this.lock = lock;
        }

        public void countUp() {
            levelCount++;
        }

        public void countDown() {
            levelCount--;
        }

        void setLockInfo(LockInfo lockInfo) {
            this.lockInfo = lockInfo;
        }

        public int getLevelCount() {
            return levelCount;
        }
    }
}
